package version

import (
	"errors"
	"strconv"
	"strings"
)

type Version struct {
	Major int
	Minor int
	Patch int
}

func Parse(v string) (*Version, error) {
	ss := strings.Split(v, ".")
	if len(ss) != 3 {
		return nil, errors.New("Version: format error")
	}
	major, err := strconv.Atoi(ss[0])
	if err != nil {
		return nil, err
	}
	minor, err := strconv.Atoi(ss[1])
	if err != nil {
		return nil, err
	}
	patch, err := strconv.Atoi(ss[2])
	if err != nil {
		return nil, err
	}
	return &Version{
		Major: major,
		Minor: minor,
		Patch: patch,
	}, nil
}

func (v *Version) String() string {
	return strconv.Itoa(v.Major) + "." + strconv.Itoa(v.Minor) + "." + strconv.Itoa(v.Patch)
}

func (v *Version) GreaterThan(other *Version) bool {
	if v.Major != other.Major {
		return v.Major > other.Major
	}
	if v.Minor != other.Minor {
		return v.Minor > other.Minor
	}
	if v.Patch != other.Patch {
		return v.Patch > other.Patch
	}
	return false
}

func (v *Version) Equals(other *Version) bool {
	return v.Major == other.Major && v.Minor == other.Minor && v.Patch == other.Patch
}

type SemVer struct {
	Version
	PreRelease string
	Build      string
}

func ParseSemVer(v string) (*SemVer, error) {
	sv := &SemVer{}
	if ss := strings.SplitN(v, "+", 2); len(ss) == 2 {
		v = ss[0]
		sv.Build = ss[1]
	}
	if ss := strings.SplitN(v, "-", 2); len(ss) == 2 {
		v = ss[0]
		sv.PreRelease = ss[1]
	}
	ver, err := Parse(v)
	if err != nil {
		return nil, err
	}
	sv.Version = *ver
	return sv, nil
}

func (v *SemVer) String() string {
	s := v.Version.String()
	if v.PreRelease != "" {
		s += "-" + v.PreRelease
	}
	if v.Build != "" {
		s += "+" + v.Build
	}
	return s
}

func (v *SemVer) GreaterThan(other *SemVer) bool {
	if v.Major != other.Major {
		return v.Major > other.Major
	}
	if v.Minor != other.Minor {
		return v.Minor > other.Minor
	}
	if v.Patch != other.Patch {
		return v.Patch > other.Patch
	}
	if v.PreRelease != other.PreRelease {
		return v.PreRelease > other.PreRelease
	}
	if v.Build != other.Build {
		return v.Build > other.Build
	}
	return false
}

func (v *SemVer) Equals(other *SemVer) bool {
	return v.Version.Equals(&other.Version) && v.PreRelease == other.PreRelease && v.Build == other.Build
}
